﻿using GetThis_Server.Properties;
using Server;
using System;
using System.Diagnostics.Contracts;
using System.Net;

namespace GetThis_Server.Options
{
    internal static class ApplicationParametersHelper
    {
        internal static void Save(this ApplicationParameters applicationParameters)
        {
            Contract.Requires<ArgumentNullException>(applicationParameters != null);

            Settings.Default.LogLines = applicationParameters.LogLines;
            Settings.Default.DataPath = applicationParameters.DataPath;
            ServerParameters parameters = applicationParameters.ServerParameters;
            Settings.Default.ConnectionPort = parameters.ConnectionPort;
            Settings.Default.ClientVersion = parameters.ClientVersion;
            Settings.Default.UpdateAddress = parameters.UpdateAddress;
            Settings.Default.ServerPort = parameters.ServerPort;
            Settings.Default.PendingConnectionsQueueLength = parameters.PendingConnectionsQueueLength;
            Settings.Default.ServerIdleTime = parameters.ServerIdleTime;
            Settings.Default.VerifyConnectionInterval = parameters.VerifyConnectionInterval;
            Settings.Default.ClientTimeout = parameters.ClientTimeout;
            Settings.Default.ClientManagerIdleTime = parameters.ClientManagerIdleTime;
            Settings.Default.Save();
        }

        internal static bool TryLoadAll(this ApplicationParameters applicationParameters, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(logAction != null);
            Contract.Requires<ArgumentNullException>(applicationParameters != null);

            Settings settings = Settings.Default;
            ServerParameters serverParameters = applicationParameters.ServerParameters;

            bool result = applicationParameters.TryLoad(logAction);
            result = serverParameters.SetConnectionPort(settings.ConnectionPort, logAction) && result;
            result = serverParameters.SetClientVersion(settings.ClientVersion, logAction) && result;
            result = serverParameters.SetUpdateAddress(settings.UpdateAddress, logAction) && result;
            result = serverParameters.SetServerPort(settings.ServerPort, logAction) && result;
            result = serverParameters.SetPendingConnectionsQueueLength(settings.PendingConnectionsQueueLength, logAction) && result;
            result = serverParameters.SetServerIdleTime(settings.ServerIdleTime, logAction) && result;
            result = serverParameters.SetVerifyConnectionInterval(settings.VerifyConnectionInterval, logAction) && result;
            result = serverParameters.SetClientTimeout(settings.ClientTimeout, logAction) && result;
            return serverParameters.SetClientManagerIdleTime(settings.ClientManagerIdleTime, logAction) && result;
        }

        internal static bool TryLoad(this ApplicationParameters applicationParameters, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(logAction != null);
            Contract.Requires<ArgumentNullException>(applicationParameters != null);

            Settings settings = Settings.Default;

            // reload the settings
            settings.Reload();

            bool result = applicationParameters.SetLogLines(settings.LogLines, logAction);
            return applicationParameters.SetDataPath(settings.DataPath, logAction) && result;
        }

        /// <summary>
        /// Sets the maximum number of lines in the log.
        /// </summary>
        /// <param name="applicationParameters">The <see cref="T:GetThis_Server.ApplicationParameters" />.</param>
        /// <param name="logLines">The maximum number of lines in the log.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetLogLines(this ApplicationParameters applicationParameters, int logLines, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(applicationParameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (logLines > 0)
            {
                applicationParameters.LogLines = logLines;
                return true;
            }
            logAction(String.Format("Log lines must be a positive value (defined value : {0}).", logLines));
            return false;
        }

        /// <summary>
        /// Sets the path of the data file.
        /// </summary>
        /// <param name="applicationParameters">The <see cref="T:GetThis_Server.ApplicationParameters" />.</param>
        /// <param name="dataPath">The path of the data file.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetDataPath(this ApplicationParameters applicationParameters, string dataPath, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(applicationParameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (String.IsNullOrWhiteSpace(dataPath))
            {
                logAction("Data path must be defined.");
                return false;
            }
            applicationParameters.DataPath = dataPath;
            return true;
        }

        /// <summary>
        /// Sets the port on which to listen for incoming connection attempts.
        /// </summary>
        /// <param name="applicationParameters">The <see cref="T:GetThis_Server.ApplicationParameters" />.</param>
        /// <param name="connectionPort">The port on which to listen for incoming connection attempts.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetConnectionPort(this ServerParameters parameters, int connectionPort, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(parameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (connectionPort >= IPEndPoint.MinPort && connectionPort <= IPEndPoint.MaxPort)
            {
                parameters.ConnectionPort = connectionPort;
                return true;
            }
            logAction(String.Format("Connection port is not between {0} and {1} (defined value : {2}).", IPEndPoint.MinPort, IPEndPoint.MaxPort, connectionPort));
            return false;
        }

        /// <summary>
        /// Sets the current version of the client software.
        /// </summary>
        /// <param name="parameters">The <see cref="T:Server.Parameters" />.</param>
        /// <param name="clientVersion">The current version of the client software.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetClientVersion(this ServerParameters parameters, string clientVersion, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(parameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (String.IsNullOrWhiteSpace(clientVersion))
            {
                logAction("Client version must be defined.");
                return false;
            }
            parameters.ClientVersion = clientVersion;
            return true;
        }

        /// <summary>
        /// Sets the address to use in order to update the client software.
        /// </summary>
        /// <param name="parameters">The <see cref="T:Server.Parameters" />.</param>
        /// <param name="updateAddress">The address to use in order to update the client software.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetUpdateAddress(this ServerParameters parameters, string updateAddress, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(parameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (String.IsNullOrWhiteSpace(updateAddress))
            {
                logAction("Update address must be defined.");
                return false;
            }
            parameters.UpdateAddress = updateAddress;
            return true;
        }

        /// <summary>
        /// Sets the port on which to listen for client communication attempts.
        /// </summary>
        /// <param name="parameters">The <see cref="T:Server.Parameters" />.</param>
        /// <param name="serverPort">The port on which to listen for client communication attempts.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetServerPort(this ServerParameters parameters, int serverPort, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(parameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (serverPort >= IPEndPoint.MinPort && serverPort <= IPEndPoint.MaxPort)
            {
                parameters.ServerPort = serverPort;
                return true;
            }
            logAction(String.Format("Server port is not between {0} and {1} (defined value : {2}).", IPEndPoint.MinPort, IPEndPoint.MaxPort, serverPort));
            return false;
        }

        /// <summary>
        /// Sets the maximum length of the pending connections queue.
        /// </summary>
        /// <param name="parameters">The <see cref="T:Server.Parameters" />.</param>
        /// <param name="pendingConnectionsQueueLength">The maximum length of the pending connections queue.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetPendingConnectionsQueueLength(this ServerParameters parameters, int pendingConnectionsQueueLength, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(parameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (pendingConnectionsQueueLength >= 0)
            {
                parameters.PendingConnectionsQueueLength = pendingConnectionsQueueLength;
                return true;
            }
            logAction(String.Format("Pending connections queue length must be a positive value or zero (defined value : {0}).", pendingConnectionsQueueLength));
            return false;
        }

        /// <summary>
        /// Sets the number of milliseconds during which the <see cref="T:Server.TcpServer" /> will be sleeping when it has no work to do.
        /// </summary>
        /// <param name="parameters">The <see cref="T:Server.Parameters" />.</param>
        /// <param name="serverIdleTime">The number of milliseconds during which the <see cref="T:Server.TcpServer" /> will be sleeping when it has no work to do.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetServerIdleTime(this ServerParameters parameters, int serverIdleTime, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(parameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (serverIdleTime >= 0)
            {
                parameters.ServerIdleTime = serverIdleTime;
                return true;
            }
            logAction(String.Format("Server idle time must be a positive value or zero (defined value : {0}).", serverIdleTime));
            return false;
        }

        /// <summary>
        /// Sets how often (in milliseconds) the <see cref="T:Server.TcpServer" /> should verify that a client is still connected.
        /// </summary>
        /// <param name="parameters">The <see cref="T:Server.Parameters" />.</param>
        /// <param name="verifyConnectionInterval">How often (in milliseconds) the <see cref="T:Server.TcpServer" /> should verify that a client is still connected.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetVerifyConnectionInterval(this ServerParameters parameters, int verifyConnectionInterval, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(parameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (verifyConnectionInterval >= 0)
            {
                parameters.VerifyConnectionInterval = verifyConnectionInterval;
                return true;
            }
            logAction(String.Format("Verify connection interval must be a positive value or zero (defined value : {0}).", verifyConnectionInterval));
            return false;
        }

        /// <summary>
        /// Sets the number of milliseconds to wait before the connection with a client is considered to be lost.
        /// </summary>
        /// <param name="parameters">The <see cref="T:Server.Parameters" />.</param>
        /// <param name="clientTimeout">The number of milliseconds to wait before the connection with a client is considered to be lost.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetClientTimeout(this ServerParameters parameters, int clientTimeout, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(parameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (clientTimeout >= 0)
            {
                parameters.ClientTimeout = clientTimeout;
                return true;
            }
            logAction(String.Format("Client timeout must be a positive value or zero (defined value : {0}).", clientTimeout));
            return false;
        }

        /// <summary>
        /// Sets the number of milliseconds during which the <see cref="T:Server.TcpClientManager" /> will be sleeping when it has no work to do.
        /// </summary>
        /// <param name="parameters">The <see cref="T:Server.Parameters" />.</param>
        /// <param name="clientManagerIdleTime">The number of milliseconds during which the <see cref="T:Server.TcpClientManager" /> will be sleeping when it has no work to do.</param>
        /// <param name="logAction">Logging <see cref="T:System.Action`1" />.</param>
        /// <returns><c>true</c> if the provided value was valid; otherwise <c>false</c>.</returns>
        internal static bool SetClientManagerIdleTime(this ServerParameters parameters, int clientManagerIdleTime, Action<string> logAction)
        {
            Contract.Requires<ArgumentNullException>(parameters != null);
            Contract.Requires<ArgumentNullException>(logAction != null);

            if (clientManagerIdleTime >= 0)
            {
                parameters.ClientManagerIdleTime = clientManagerIdleTime;
                return true;
            }
            logAction(String.Format("Client manager idle time must be a positive value or zero (defined value : {0}).", clientManagerIdleTime));
            return false;
        }
    }
}
